package com.rzgeo.wcgallay.model;

import android.content.Context;

import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.FeatureQueryResult;
import com.esri.arcgisruntime.data.FeatureTable;
import com.esri.arcgisruntime.data.Field;
import com.esri.arcgisruntime.data.Geodatabase;
import com.esri.arcgisruntime.data.GeodatabaseFeatureTable;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.data.ShapefileFeatureTable;
import com.esri.arcgisruntime.data.TileCache;
import com.esri.arcgisruntime.geometry.Envelope;
import com.esri.arcgisruntime.geometry.GeometryEngine;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.layers.ArcGISTiledLayer;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.layers.Layer;
import com.esri.arcgisruntime.mapping.LayerList;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.rzgeo.rzmaplib.comp.RmlMarkList;
import com.rzgeo.rzmaplib.comp.RmlSlideList;
import com.rzgeo.wcgallay.WcmChartActv;
import com.rzgeo.wcgallay.comp.WcmReseDetail;
import com.rzgeo.wcgallay.comp.WcmReseGallary;
import com.rzgeo.wcgallay.util.WcmEnv;
import com.rzgeo.wcgallay.util.WcmUtil;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by zhanghang on 2017/12/10.
 */
public class WcmMapModel {
    public static final String KMapMode = "MapMode";
    public static String V2DMap = "2d";
    public static String V3DScene = "3d";
    private final Context _context;
    private final RmlMarkList _markList;
    private final RmlSlideList _slideList;


    private final WcmReseGallary _reseGallary;
    private final GeoViewHolder _geoViewHolder;
    private final WcmChartActv.StartInfo.StartInfoBuilder _startInfoBuilder;

    private FeatureLayer _markPonLayer;
    private FeatureTable _slideViewPon;
    private FeatureTable _slideGroupLine;
    private final WcmReseGallary.OnSelect _onReseSelected = new WcmReseGallary.OnSelect() {


        @Override
        public void onChangedDemColors(int iPart, File tpk) {

            PartHolder ph = _partHolders[iPart];
            if (ph.demLayers.size() > 0) {
                Layer l = ph.demLayers.get(0);
                LayerList ls = _geoViewHolder.getDemRasterLayer();
                int i = ls.indexOf(l);
                if (i > -1) {
//                    ls.remove(i);
                    TileCache tileCache = new TileCache(tpk.getAbsolutePath());
                    ArcGISTiledLayer tlayer = new ArcGISTiledLayer(tileCache);
                    ls.set(i, tlayer);

                    i = ph.demLayers.indexOf(l);
                    ph.demLayers.set(i, tlayer);
                }
            }
        }

        @Override
        public void onViewAtRese(WcmSysFolder.WcmReseFolder.WcmPart rese, final int iPart, int currentElv) {

            try {
                Point llPoint = null;

                PartHolder ph = _partHolders[iPart];
                if (ph.domLayers.size() > 0) {
                    Layer l = ph.domLayers.get(0);
                    llPoint = (Point) GeometryEngine.project(
                            l.getFullExtent().getCenter(),
                            WcmUtil.SR.defaultGeoSR);
                } else if (ph.demLayers.size() > 0) {
                    Layer l = ph.demLayers.get(0);
                    llPoint = (Point) GeometryEngine.project(
                            l.getFullExtent().getCenter(),
                            WcmUtil.SR.defaultGeoSR);
                }

                if (llPoint != null) {
                    _geoViewHolder.setViewPoint(llPoint);
                }

                _reseDetail.setup(
                        ph.gdb.getGeodatabaseFeatureTable(KTableName),
                        _gls,
                        currentElv,
                        new WcmReseDetail.OnDetail() {
                            @Override
                            public void onElvChanged(ArrayList<ElvInfo> elv, int iElv) {
                                _reseGallary.setPartValue(iPart, elv, iElv);

                            }
                        });
            } catch (Exception e) {
                WcmUtil.Log.Msg(e.getMessage());
            }
        }

        @Override
        public void onSwitchElvRand(int i, boolean isChecked) {
            try {

                PartHolder ph = _partHolders[i];
                if (ph.demLayers != null && ph.demLayers.size() > 0) {
                    Layer layer = ph.demLayers.get(ph.demLayers.size() - 1);
                    layer.setVisible(isChecked);
                }
            } catch (Exception e) {
                WcmUtil.Log.Msg(e.getMessage());
            }
        }
    };
    private final WcmReseDetail _reseDetail;
    private final GraphicsOverlay _gls;
    private final String _prjName;

    private WcmSysFolder.WcmReseFolder _currentResr;

    private static final String KTableName = "elv2info";

    public WcmMapModel(Context context,
                       GeoViewHolder geoViewHolder,
                       String prjName,
                       WcmReseGallary reseGallary,
                       WcmReseDetail reseDetail,
                       RmlMarkList markList,
                       RmlSlideList slideList)
    {
        _prjName = prjName;
        _geoViewHolder = geoViewHolder;

        _reseGallary = reseGallary;
        _reseDetail = reseDetail;
        _markList=markList;
        _slideList=slideList;

        _gls = new GraphicsOverlay();
        _geoViewHolder.addGraphicsOverlay(_gls);
        _context = context;

        _startInfoBuilder =new WcmChartActv.StartInfo.StartInfoBuilder(
                _prjName,
                new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        double d1=Double.valueOf(o1);
                        double d2=Double.valueOf(o2);
                        if(d1==d2)
                            return 0;
                        else if(d1>d2)
                            return 1;
                        else
                            return -1;
                    }
                },
                0
        );

        WcmSysFolder appFolder = WcmEnv.getDefaultInst().getAppStandFolder();
        //添加项目资源
        WcmSysFolder.WcmReseFolder[] reses2check = appFolder.listRese();
        for (WcmSysFolder.WcmReseFolder rese : reses2check) {
            if (_prjName.equalsIgnoreCase(rese.getName())) {
                _currentResr = rese;
                break;
            }
        }

        ArrayList<Layer> tlayers = new ArrayList<>();
        if (_currentResr.getShareDom() != null) {
            TileCache tileCache = new TileCache(_currentResr.getShareDom().getAbsolutePath());
            tlayers.add(new ArcGISTiledLayer(tileCache));
        } else {
            tlayers.add(new ArcGISTiledLayer("http://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity/MapServer"));
        }

        ArcGISTiledLayer tlayer = null;
        if (appFolder.getGrobleLayer().exists()) {
            TileCache tileCache = new TileCache(appFolder.getGrobleLayer().getAbsolutePath());
            tlayer = new ArcGISTiledLayer(tileCache);
        }

        if (tlayer != null) {
            _geoViewHolder.setBaseMap(tlayer, _currentResr.getShareDem().getAbsolutePath());
            _geoViewHolder.addDomLayer(tlayers);
        } else {
            _geoViewHolder.setBaseMap((ArcGISTiledLayer) tlayers.get(0), _currentResr.getShareDem().getAbsolutePath());
        }


        //ArcGISTiledLayer tlayer = new ArcGISTiledLayer("http://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity/MapServer");
        //ArcGISTiledLayer baselayer = new ArcGISTiledLayer("http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer");
        //_basemap = new Basemap(baselayer);
        //_basemap.add();


        //添加部件
        _reseGallary.setup(_onReseSelected);

        _reseGallary.setData(_currentResr.getWcmParts(), 0);

        loadPart2Map(_currentResr.getWcmParts());

        loadToolLayers(appFolder);
    }

    public WcmChartActv.StartInfo getStartInfo(){
        return _startInfoBuilder.toStartInfo();
    }

    private void loadToolLayers(WcmSysFolder appFolder) {
        WcmShapefileHelper[] fhs = new WcmShapefileHelper[]{
                new WcmShapefileHelper(appFolder.slideFolder, "mark_point", "标注点"),
                new WcmShapefileHelper(appFolder.slideFolder, "slide_groupline", "幻灯片路线"),
                new WcmShapefileHelper(appFolder.slideFolder, "slide_point", "幻灯片视点")
        };
        FeatureTable[]fts=new FeatureTable[3];
        for (int i=0;i<fhs.length;i++) {
            WcmShapefileHelper fh = fhs[i];
            if (!fh.exists()) {
                if (fh.initFromAssests(_context, "shp"))
                    WcmUtil.Tip.Toast("初始化图层："+fh.title);
                else{
                    WcmUtil.Tip.Toast("初始化失败："+fh.title);
                    break;
                }
            }
            fts[i] = new ShapefileFeatureTable(fh.shpFile.getAbsolutePath());
        }

        if(fts[0]!=null){

            _markPonLayer=new FeatureLayer(fts[0]);
            _geoViewHolder.addOptionLayer(_markPonLayer);
        }

        _slideGroupLine=fts[1];
        _slideViewPon=fts[2];

        if(_markList!=null)
            _markList.setup(_geoViewHolder,_markPonLayer);

        if(_slideList!=null){
            _slideList.setup(_slideGroupLine,_slideViewPon);
        }
    }


    private PartHolder[] _partHolders;

    private void loadPart2Map(WcmSysFolder.WcmReseFolder.WcmPart[] wcmParts) {
        _partHolders = new PartHolder[wcmParts.length];

//        ArcGISScene scene = _sceneView.getScene();

        for (int i = 0; i < wcmParts.length; i++) {
            _partHolders[i] = new PartHolder(wcmParts[i]);
            PartHolder ph = _partHolders[i];
            if (ph.part.domPaths.length > 0) {

                for (File dom : ph.part.domPaths) {

                    TileCache tileCache = new TileCache(dom.getAbsolutePath());

                    final ArcGISTiledLayer domlayer = new ArcGISTiledLayer(tileCache);

                    _domLoadListener.addLayer(domlayer);
                    domlayer.addDoneLoadingListener(_domLoadListener);

                    domlayer.setRefreshInterval(1000);

                    ph.domLayers.add(domlayer);

                    WcmUtil.Log.Msg("添加图层" + dom.getAbsolutePath());
                }
            }

            if (ph.part.demPaths.length > 0) {
                WcmSysFolder.WcmReseFolder.WcmDemWithColors dem = ph.part.demPaths[0];
                TileCache tileCache = new TileCache(dem.DemTpk.getAbsolutePath());
                ArcGISTiledLayer demlayer = new ArcGISTiledLayer(tileCache);
                ph.demLayers.add(demlayer);
            }

            if (ph.part.vecGdbFile != null) {
                ph.gdb = new Geodatabase(ph.part.vecGdbFile.getAbsolutePath());
                _gdbLoadedListener.add(ph.gdb,ph.part.getName());
                ph.gdb.addDoneLoadingListener(_gdbLoadedListener);
            }
        }


        for (PartHolder ph : _partHolders) {
            _geoViewHolder.addDomLayer(ph.domLayers);
            _geoViewHolder.addDemRasterLayer(ph.demLayers);
            ph.gdb.loadAsync();
        }


    }

    class DomLoadListener implements Runnable {

        private ArrayList<ArcGISTiledLayer> _layers = new ArrayList<>();
        private int _count;

        @Override
        public void run() {
            _count++;
            WcmUtil.Log.Msg("加载DOM图层");
            if (_count < _layers.size())
                return;

            ArcGISTiledLayer l = _layers.get(0);
            Envelope extent = l.getFullExtent();

            for (int i = 1; i < _layers.size(); i++) {
                l = _layers.get(i);
                extent = GeometryEngine.union(extent, l.getFullExtent()).getExtent();
            }

            Point llPoint = (Point) GeometryEngine.project(extent.getCenter(), WcmUtil.SR.defaultGeoSR);
//            Camera camera = new Camera(llPoint,3000,0,30,0);
//            _sceneView.setViewpointCameraAsync(camera);
            _geoViewHolder.setViewPoint(llPoint);
            _layers.clear();
            _count = 0;
        }

        public void addLayer(ArcGISTiledLayer l) {
            _layers.add(l);
        }
    }

    DomLoadListener _domLoadListener = new DomLoadListener();

    class GDBLoadListener implements Runnable {
        private int _count;
        private ArrayList<Geodatabase> _gdbs = new ArrayList<>();
        private OnQueryLoad _queryDownLoad = new OnQueryLoad();
        private Map<Geodatabase,String>gdb2Name=new HashMap<>();

        class OnQueryLoad implements Runnable {

            public ArrayList<ListenableFuture<FeatureQueryResult>> futures = new ArrayList<>();
            public Map<ListenableFuture<FeatureQueryResult>,String> futures2Map=new HashMap<>();
            private int _count;

            @Override
            public void run() {
                _count++;
                WcmUtil.Log.Msg("加载图层");
                if (_count < futures.size())
                    return;


                float volumeSum = 0;
                float minHeight = Float.MAX_VALUE;
                float maxHeight = Float.MIN_VALUE;

                for (ListenableFuture<FeatureQueryResult> future : futures) {


                    try {
                        Field volumeField = null;
                        Field heigthField = null;
                        for (Field f : future.get().getFields()) {
                            if (f.getName().endsWith("Volume")) {
                                volumeField = f;
                            } else if (f.getName().endsWith("Plane_Height")) {
                                heigthField = f;
                            }
                        }

                        if (volumeField == null) {
                            WcmUtil.Log.Msg("发现错误，没有容积字段");
                            break;
                        }
                        if (heigthField == null) {
                            WcmUtil.Log.Msg("发现错误，没有高度字段");
                            break;
                        }

                        Iterator<Feature> iterator = future.get().iterator();

                        float maxVolume = Float.MIN_VALUE;
                        String name = futures2Map.get(future);
                        while (iterator.hasNext()) {
                            Feature feature = iterator.next();
                            Map<String, Object> attrs = feature.getAttributes();
                            float volume = WcmUtil.Convert.Float(attrs.get(volumeField.getName()));
                            if (volume > maxVolume)
                                maxVolume = volume;

                            float height = WcmUtil.Convert.Float(attrs.get(heigthField.getName()));
                            if (height > maxHeight)
                                maxHeight = height;
                            if (height < minHeight)
                                minHeight = height;

                            _startInfoBuilder.addData(name,Double.toString(height),volume);
                        }
                        volumeSum += maxVolume;
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                futures.clear();
                _reseGallary.setInfo(volumeSum, minHeight, maxHeight);
            }

            void add(ListenableFuture<FeatureQueryResult> future,String name) {
                futures2Map.put(future,name);
                futures.add(future);
            }
        }

        @Override
        public void run() {
            _count++;
            WcmUtil.Log.Msg("加载图层");
            if (_count < _gdbs.size())
                return;


            for (Geodatabase gdb : _gdbs) {
                GeodatabaseFeatureTable elv2info = gdb.getGeodatabaseFeatureTable(KTableName);

                QueryParameters qp = new QueryParameters();
                qp.setReturnGeometry(false);

                ListenableFuture<FeatureQueryResult> fr = elv2info.queryFeaturesAsync(qp);
                _queryDownLoad.add(fr,gdb2Name.get(gdb));
                fr.addDoneListener(_queryDownLoad);
            }
        }

        public void add(Geodatabase gdb,String name) {
            gdb2Name.put(gdb,name);
            _gdbs.add(gdb);
        }
    }

    GDBLoadListener _gdbLoadedListener = new GDBLoadListener();

    public static class PartHolder {

        private final WcmSysFolder.WcmReseFolder.WcmPart part;
        public Geodatabase gdb;
        public ArrayList<Layer> domLayers;
        public ArrayList<Layer> demLayers;


        public PartHolder(WcmSysFolder.WcmReseFolder.WcmPart part) {
            this.part = part;

            this.domLayers = new ArrayList<>();
            this.demLayers = new ArrayList<>();

        }
    }

}
